package com.dl698.util;

import java.io.IOException;

/**
 * 数据工具类
 * 提供字节数组和十六进制字符串之间的转换功能
 * 
 * @author wind
 * @version 1.1.1
 */
public class DataUtils {

    private static final char[] HEX_ARRAY = {
            '0', '1', '2', '3', '4', '5', '6', '7',
            '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
    };

    /**
     * 从输入流中完全读取数据到缓冲区
     * 
     * @param input  输入流
     * @param buffer 缓冲区
     * @param off    偏移量
     * @param len    长度
     * @throws IOException 如果读取失败
     */
    public static void readFully(ReverseByteArrayInputStream input, byte[] buffer, int off, int len)
            throws IOException {
        do {
            int bytesRead = input.readOffset(buffer, off, len);
            if (bytesRead == -1) {
                throw new IOException("Unexpected end of input stream.");
            }
            len -= bytesRead;
            off += bytesRead;
        } while (len > 0);
    }

    /**
     * 将十六进制字符串转换为字节数组
     * 
     * @param value 十六进制字符串
     * @return 字节数组
     * @throws IllegalArgumentException 如果字符串格式不正确
     */
    public static byte[] toBytes(String value) {
        if (value == null || value.trim().isEmpty()) {
            throw new IllegalArgumentException("value is null or empty");
        }

        value = value.trim();
        if (value.length() % 2 != 0) {
            throw new IllegalArgumentException("value.length % 2 != 0");
        }

        byte[] ret = new byte[value.length() / 2];
        for (int i = 0; i < ret.length; i++) {
            try {
                String tmp = value.substring(i * 2, i * 2 + 2);
                ret[i] = (byte) (Integer.parseInt(tmp, 16) & 0xFF);
            } catch (NumberFormatException e) {
                throw new IllegalArgumentException("cast exception", e);
            }
        }
        return ret;
    }

    /**
     * 将十六进制字符串转换为字节数组
     * 
     * @param hex 十六进制字符串
     * @return 字节数组
     * @throws IllegalArgumentException 如果字符串为空
     */
    public static byte[] hexToBytes(String hex) {
        if (hex == null || hex.isEmpty()) {
            throw new IllegalArgumentException("hex is null or empty");
        }

        byte[] result = new byte[hex.length() / 2];
        int j = 0;
        for (int i = 0; i < hex.length(); i += 2) {
            result[j++] = (byte) Integer.parseInt(hex.substring(i, i + 2), 16);
        }
        return result;
    }

    /**
     * 将整数转换为十六进制字符串
     * 
     * @param value       整数值
     * @param bytesLength 字节长度（1、2或4）
     * @return 十六进制字符串，如果字节长度无效则返回null
     */
    public static String intToHexString(int value, int bytesLength) {
        if (bytesLength != 1 && bytesLength != 2 && bytesLength != 4) {
            return null;
        }

        int mask;
        switch (bytesLength) {
            case 1:
                mask = 0x000000FF;
                break;
            case 2:
                mask = 0x0000FFFF;
                break;
            case 4:
                mask = 0xFFFFFFFF;
                break;
            default:
                return null;
        }

        int length = 2 * bytesLength;
        String ret = Integer.toHexString(value & mask);
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < (length - ret.length()); i++) {
            sb.append("0");
        }
        sb.append(ret);
        return sb.toString().toUpperCase();
    }

    /**
     * 将字节数组转换为十六进制字符串
     * 
     * @param buf    字节数组
     * @param offset 偏移量
     * @param length 长度
     * @return 十六进制字符串，如果参数无效则返回null
     */
    public static String bytesToHexString(byte[] buf, int offset, int length) {
        if (buf == null || offset < 0 || length < 0 || offset + length > buf.length) {
            return null;
        }

        StringBuilder retBuffer = new StringBuilder();
        for (int i = offset; i < offset + length; i++) {
            retBuffer.append(byteToHexString(buf[i]));
        }
        return retBuffer.toString().toUpperCase();
    }

    /**
     * 将单个字节转换为十六进制字符串
     * 
     * @param value 字节值
     * @return 十六进制字符串
     */
    public static String byteToHexString(int value) {
        String tmp = Integer.toHexString(value & 0xFF).toUpperCase();
        return tmp.length() % 2 == 1 ? "0" + tmp : tmp;
    }

    /**
     * 将字节数组转换为十六进制字符串
     * 
     * @param buf         字节数组
     * @param bytesLength 字节长度（1、2或4）
     * @param length      起始位置
     * @return 十六进制字符串
     * @throws IllegalArgumentException 如果字节长度无效
     */
    public static String toHexString(byte[] buf, int bytesLength, int length) {
        if (bytesLength != 1 && bytesLength != 2 && bytesLength != 4) {
            throw new IllegalArgumentException("bytesLength not 1，2，4");
        }

        StringBuilder sb = new StringBuilder();
        for (int i = length; i < buf.length; i++) {
            int mask;
            switch (bytesLength) {
                case 1:
                    mask = 255;
                    break;
                case 2:
                    mask = 65535;
                    break;
                case 4:
                    mask = -1;
                    break;
                default:
                    mask = 255;
            }

            int hexLength = 2 * bytesLength;
            int b = buf[i] & mask;
            String ret = Integer.toHexString(b);
            for (int j = 0; j < hexLength - ret.length(); j++) {
                sb.append("0");
            }
            sb.append(ret);
        }
        return sb.toString().toUpperCase();
    }

    /**
     * 获取字符在指定进制下的数字值
     * 
     * @param codePoint 字符编码点
     * @param radix     进制
     * @return 数字值，如果无效则返回-1
     */
    public static int digit(int codePoint, int radix) {
        if (radix < 2 || radix > 36) {
            return -1;
        }

        if (codePoint < 128) {
            int result = -1;
            if ('0' <= codePoint && codePoint <= '9') {
                result = codePoint - '0';
            } else if ('a' <= codePoint && codePoint <= 'z') {
                result = 10 + (codePoint - 'a');
            } else if ('A' <= codePoint && codePoint <= 'Z') {
                result = 10 + (codePoint - 'A');
            }
            return result < radix ? result : -1;
        }
        return -1;
    }

    /**
     * 将字节数组转换为短十六进制字符串
     * 
     * @param bytes  字节数组
     * @param offset 偏移量
     * @param length 长度
     * @param revert 是否反转
     * @return 十六进制字符串
     */
    public static String toShortHexString(byte[] bytes, int offset, int length, boolean revert) {
        char[] hexChars = new char[length * 2];
        for (int j = offset; j < offset + length; j++) {
            int v = bytes[j] & 0xFF;
            if (revert) {
                hexChars[length * 2 - (j - offset) * 2 - 2] = HEX_ARRAY[v >>> 4];
                hexChars[length * 2 - (j - offset) * 2 - 1] = HEX_ARRAY[v & 0xF];
            } else {
                hexChars[(j - offset) * 2] = HEX_ARRAY[v >>> 4];
                hexChars[(j - offset) * 2 + 1] = HEX_ARRAY[v & 0xF];
            }
        }
        return new String(hexChars);
    }

    /**
     * 从短十六进制字符串转换为字节数组
     * 
     * @param str    十六进制字符串
     * @param revert 是否反转
     * @return 字节数组
     * @throws IllegalArgumentException 如果字符串格式不正确
     */
    public static byte[] fromShortHexString(String str, boolean revert) {
        if (str == null) {
            throw new IllegalArgumentException("string s may not be null");
        }

        str = str.trim().replace(" ", "");
        if (str.isEmpty() || (str.length() % 2) != 0) {
            throw new IllegalArgumentException("string is not a legal hex string.");
        }

        int length = str.length();
        byte[] data = new byte[length / 2];
        for (int i = 0; i < length; i += 2) {
            int firstCharacter = digit(str.charAt(i), 16);
            int secondCharacter = digit(str.charAt(i + 1), 16);
            if (firstCharacter == -1 || secondCharacter == -1) {
                throw new IllegalArgumentException("string is not a legal hex string.");
            }

            if (revert) {
                data[(length - i) / 2 - 1] = (byte) ((firstCharacter << 4) + secondCharacter);
            } else {
                data[i / 2] = (byte) ((firstCharacter << 4) + secondCharacter);
            }
        }
        return data;
    }
}